/*
 * Copyright (c) 2024 Black Sesame Technologies
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <tinyxml2.h>

#include "dal_fastdds/converter/QosConverter.hpp"
#include "dal_fastdds/converter/LocatorKindConverter.hpp"
#include "dal_fastdds/utils/StringUtils.hpp"
#include "dal_fastdds/utils/log.hpp"
#include "dal_fastdds/config_manager/ConfigManager.hpp"

#include <fastdds/rtps/transport/UDPv4TransportDescriptor.h>
#ifndef __QNXNTO__
#    include <fastdds/rtps/transport/shared_mem/SharedMemTransportDescriptor.h>
#endif

using namespace tinyxml2;

QosConverter* QosConverter::singleton_ = nullptr;

QosConverter::QosConverter()
{
    is_flow_controller_profiles_exist = false;
    flow_controller_name = "";
}

QosConverter* QosConverter::GetInstance()
{
    if (singleton_ == nullptr) {
        singleton_ = new QosConverter();
    }
    return singleton_;
}

dal_ret_t QosConverter::SetMulticastLocatorsToFastddsParticipantQos(SEQUENCE_BASE* seq,
                                                                    DomainParticipantQos* f_qos)
{
    eprosima::fastrtps::rtps::Locator_t f_locator;
    uint32_t multicast_locator_num = SIZE_SEQUENCE(dal_locator_t, seq);

    if (multicast_locator_num == 0) {
        eprosima::fastrtps::rtps::IPLocator::setIPv4(f_locator, "239.255.0.1");
        f_qos->wire_protocol().builtin.metatrafficMulticastLocatorList.push_back(f_locator);
        return DAL_RETCODE_OK;
    }

    dal_locator_t* d_locator = (dal_locator_t*)calloc(sizeof(dal_locator_t), 1);
    for (int i = 0; i < multicast_locator_num; i++) {
        memset(d_locator, 0, sizeof(dal_locator_t));
        GET_ELEMENT_SEQUENCE(dal_locator_t, seq, i, d_locator);
        f_locator.kind = LocatorKindConverter::GetInstance()->ConvertLocatorkindFromDalToFastdds(
            (*d_locator).kind);

        auto ip_str = std::string((const char*)(d_locator->address),
                                  GetStringLengthInByteArray(d_locator->address));
        auto ips = Split(ip_str, '.');
        if (ips.size() != 4) {
            LOG_ERROR_WITH_LOCATION() << "multicast_locator invalid address: " << LOG_ENDL;
            free(d_locator);
            return DAL_RETCODE_BAD_PARAMETER;
        }

        eprosima::fastrtps::rtps::IPLocator::setIPv4(f_locator,
                                                     (octet)std::stoi(ips.at(0)),
                                                     (octet)std::stoi(ips.at(1)),
                                                     (octet)std::stoi(ips.at(2)),
                                                     (octet)std::stoi(ips.at(3)));
        f_qos->wire_protocol().builtin.metatrafficMulticastLocatorList.push_back(f_locator);
    }
    free(d_locator);

    return DAL_RETCODE_OK;
}

dal_ret_t QosConverter::SetUnicastLocatorsToFastddsParticipantQos(SEQUENCE_BASE* seq,
                                                                  DomainParticipantQos* f_qos)
{
    eprosima::fastrtps::rtps::Locator_t f_locator;
    uint32_t unicast_locator_num = SIZE_SEQUENCE(dal_locator_t, seq);

    if (unicast_locator_num == 0)
        return DAL_RETCODE_OK;

    dal_locator_t* d_locator = (dal_locator_t*)calloc(sizeof(dal_locator_t), 1);
    for (int i = 0; i < unicast_locator_num; i++) {
        memset(d_locator, 0, sizeof(dal_locator_t));
        GET_ELEMENT_SEQUENCE(dal_locator_t, seq, i, d_locator);
        f_locator.kind = LocatorKindConverter::GetInstance()->ConvertLocatorkindFromDalToFastdds(
            d_locator->kind);

        auto ip_str = std::string((const char*)(d_locator->address),
                                  GetStringLengthInByteArray(d_locator->address));
        auto ips = Split(ip_str, '.');
        if (ips.size() != 4) {
            LOG_ERROR_WITH_LOCATION() << "unicast_locator invalid address: " << LOG_ENDL;
            free(d_locator);
            return DAL_RETCODE_BAD_PARAMETER;
        }

        eprosima::fastrtps::rtps::IPLocator::setIPv4(f_locator,
                                                     (octet)std::stoi(ips.at(0)),
                                                     (octet)std::stoi(ips.at(1)),
                                                     (octet)std::stoi(ips.at(2)),
                                                     (octet)std::stoi(ips.at(3)));
        f_qos->wire_protocol().default_unicast_locator_list.push_back(f_locator);
        f_qos->wire_protocol().builtin.metatrafficUnicastLocatorList.push_back(f_locator);
    }
    free(d_locator);

    return DAL_RETCODE_OK;
}

void QosConverter::AddUnicastLocatorsToWhiteList(SEQUENCE_BASE* seq, DomainParticipantQos* f_qos)
{
    uint32_t unicast_locator_num = SIZE_SEQUENCE(dal_locator_t, seq);
    if (unicast_locator_num == 0)
        return;

    dal_locator_t* d_locator = (dal_locator_t*)calloc(sizeof(dal_locator_t), 1);
    for (int i = 0; i < unicast_locator_num; i++) {
        memset(d_locator, 0, sizeof(dal_locator_t));
        GET_ELEMENT_SEQUENCE(dal_locator_t, seq, i, d_locator);
        auto ip_str = std::string((const char*)(d_locator->address),
                                  GetStringLengthInByteArray(d_locator->address));
        std::shared_ptr<eprosima::fastdds::rtps::UDPv4TransportDescriptor> udp_transport =
            std::make_shared<eprosima::fastdds::rtps::UDPv4TransportDescriptor>();
        udp_transport->interfaceWhiteList.push_back(ip_str);
        f_qos->transport().user_transports.push_back(udp_transport);
    }
#ifndef __QNXNTO__
    auto shm_transport = std::make_shared<eprosima::fastdds::rtps::SharedMemTransportDescriptor>();
    f_qos->transport().user_transports.push_back(shm_transport);
#endif
    free(d_locator);
}

inline std::string GetFlowControlProfilePathFromFastddsProfilePath(const std::string& path)
{
    if (path.empty())
        return "";

    uint32_t r_idx = 0;
    if ((r_idx = path.rfind('/', path.length())) == std::string::npos) {
        return "fastdds_flowcontroller_profiles.xml";
    }

    return path.substr(0, r_idx + 1).append("fastdds_flowcontroller_profiles.xml");
}

void QosConverter::LoadFlowControlProfilesFromPathToParticipantQos(DomainParticipantQos* f_qos)
{
    auto flowcontrol_profiles_path =
        GetFlowControlProfilePathFromFastddsProfilePath(ConfigManager::GetInstance()->getFastddsProfilePath());

    if (flowcontrol_profiles_path.empty())
        return;

    XMLDocument doc;
    XMLError xml_ret;
    try {
        xml_ret = doc.LoadFile(flowcontrol_profiles_path.c_str());
    }
    catch (...) {
        LOG_ERROR_WITH_LOCATION() << "Error loading flowcontrol XML file" << LOG_ENDL;
        return;
    }

    auto flow_control = std::make_shared<eprosima::fastdds::rtps::FlowControllerDescriptor>();

    if (xml_ret != XML_SUCCESS) {
        LOG_ERROR_WITH_LOCATION() << "Error loading flowcontrol XML file" << LOG_ENDL;
    }

    is_flow_controller_profiles_exist = true;

    auto root = doc.RootElement();
    if (!root)
        return;

    auto name_elem = root->FirstChildElement("name");
    if (name_elem) {
        flow_controller_name = std::string(name_elem->GetText());
        flow_control->name = flow_controller_name.c_str();
    }

    auto scheduler_elem = root->FirstChildElement("scheduler");
    if (scheduler_elem) {
        auto scheduler = std::string(scheduler_elem->GetText());
        if (scheduler.compare("ROUND_ROBIN") == 0) {
            flow_control->scheduler =
                eprosima::fastdds::rtps::FlowControllerSchedulerPolicy::ROUND_ROBIN;
        }
        else if (scheduler.compare("HIGH_PRIORITY") == 0) {
            flow_control->scheduler =
                eprosima::fastdds::rtps::FlowControllerSchedulerPolicy::HIGH_PRIORITY;
        }
        else if (scheduler.compare("PRIORITY_WITH_RESERVATION") == 0) {
            flow_control->scheduler =
                eprosima::fastdds::rtps::FlowControllerSchedulerPolicy::PRIORITY_WITH_RESERVATION;
        }
        else {
            flow_control->scheduler = eprosima::fastdds::rtps::FlowControllerSchedulerPolicy::FIFO;
        }
    }

    auto max_bytes_elem = root->FirstChildElement("max_bytes_per_period");
    if (max_bytes_elem) {
        flow_control->max_bytes_per_period = max_bytes_elem->IntText();
    }

    auto period_elem = root->FirstChildElement("period_ms");
    if (period_elem) {
        flow_control->period_ms = period_elem->IntText();
    }

    f_qos->flow_controllers().push_back(flow_control);
}

dal_ret_t QosConverter::ConvertParticipantQosFromDalToFastdds(dal_domain_participant_qos_t* d_qos,
                                                              DomainParticipantQos* f_qos)
{
    
    if (d_qos == DAL_PARTICIPANT_QOS_DEFAULT) {
        (*f_qos) = PARTICIPANT_QOS_DEFAULT;
        return DAL_RETCODE_OK;
    }

    int seq_size = SIZE_SEQUENCE(octet, &d_qos->user_data.value);
    if (0l != seq_size) {
        std::vector<eprosima::fastrtps::rtps::octet> vec;
        for (int i = 0; i < seq_size; i++) {
            octet val = 0;
            GET_ELEMENT_SEQUENCE(octet, &d_qos->user_data.value, i, &val);
            vec.push_back(val);
        }
        f_qos->user_data().data_vec(vec);
    }

    f_qos->entity_factory(d_qos->entity_factory.autoenable_created_entities);

    if (d_qos->participant_name) {
        f_qos->name(std::string(d_qos->participant_name));
    }

    if (SIZE_SEQUENCE(dal_locator_t, &d_qos->unicast_locator_seq) > 0) {
        f_qos->transport().use_builtin_transports = false;
    }
    
    if (DAL_RETCODE_OK !=
        SetMulticastLocatorsToFastddsParticipantQos(&d_qos->multicast_locator_seq, f_qos)) {
        return DAL_RETCODE_BAD_PARAMETER;
    }

    if (DAL_RETCODE_OK !=
        SetUnicastLocatorsToFastddsParticipantQos(&d_qos->unicast_locator_seq, f_qos)) {
        return DAL_RETCODE_BAD_PARAMETER;
    }

    AddUnicastLocatorsToWhiteList(&d_qos->unicast_locator_seq, f_qos);

    LoadFlowControlProfilesFromPathToParticipantQos(f_qos);

    return DAL_RETCODE_OK;
}

dal_ret_t QosConverter::ConvertPublisherQosFromDalToFastdds(dal_publisher_qos_t* d_qos,
                                                            PublisherQos* f_qos)
{
    if (d_qos == DAL_PUBLISHER_QOS_DEFAULT) {
        (*f_qos) = PUBLISHER_QOS_DEFAULT;
        return DAL_RETCODE_OK;
    }

    switch (d_qos->presentation.access_scope) {
        default:
        case DAL_INSTANCE_PRESENTATION_QOS:
            f_qos->presentation().access_scope = INSTANCE_PRESENTATION_QOS;
            break;
        case DAL_TOPIC_PRESENTATION_QOS:
            f_qos->presentation().access_scope = TOPIC_PRESENTATION_QOS;
            break;
        case DAL_GROUP_PRESENTATION_QOS:
            f_qos->presentation().access_scope = GROUP_PRESENTATION_QOS;
            break;
    }
    f_qos->presentation().coherent_access = d_qos->presentation.coherent_access;
    f_qos->presentation().ordered_access = d_qos->presentation.ordered_access;

    // ! TODO: dal_partition_qos_policy_t

    int seq_size = SIZE_SEQUENCE(octet, &d_qos->group_data.value);
    if (0l != seq_size) {
        std::vector<eprosima::fastrtps::rtps::octet> vec;
        for (int i = 0; i < seq_size; i++) {
            octet val = 0;
            GET_ELEMENT_SEQUENCE(octet, &d_qos->group_data.value, i, &val);
            vec.push_back(val);
        }
        f_qos->group_data().data_vec(vec);
    }

    f_qos->entity_factory().autoenable_created_entities =
        d_qos->entity_factory.autoenable_created_entities;

    return DAL_RETCODE_OK;
}

dal_ret_t QosConverter::ConvertSubscriberQosFromDalToFastdds(dal_subscriber_qos_t* d_qos,
                                                             SubscriberQos* f_qos)
{
    if (d_qos == DAL_SUBSCRIBER_QOS_DEFAULT) {
        (*f_qos) = SUBSCRIBER_QOS_DEFAULT;
        return DAL_RETCODE_OK;
    }

    switch (d_qos->presentation.access_scope) {
        default:
        case DAL_INSTANCE_PRESENTATION_QOS:
            f_qos->presentation().access_scope = INSTANCE_PRESENTATION_QOS;
            break;
        case DAL_TOPIC_PRESENTATION_QOS:
            f_qos->presentation().access_scope = TOPIC_PRESENTATION_QOS;
            break;
        case DAL_GROUP_PRESENTATION_QOS:
            f_qos->presentation().access_scope = GROUP_PRESENTATION_QOS;
            break;
    }
    f_qos->presentation().coherent_access = d_qos->presentation.coherent_access;
    f_qos->presentation().ordered_access = d_qos->presentation.ordered_access;

    // ! TODO: dal_partition_qos_policy_t

    int seq_size = SIZE_SEQUENCE(octet, &d_qos->group_data.value);
    if (0l != seq_size) {
        std::vector<eprosima::fastrtps::rtps::octet> vec;
        for (int i = 0; i < seq_size; i++) {
            octet val = 0;
            GET_ELEMENT_SEQUENCE(octet, &d_qos->group_data.value, i, &val);
            vec.push_back(val);
        }
        f_qos->group_data().data_vec(vec);
    }

    f_qos->entity_factory().autoenable_created_entities =
        d_qos->entity_factory.autoenable_created_entities;

    return DAL_RETCODE_OK;
}

dal_ret_t QosConverter::ConvertTopicQosFromDalToFastdds(dal_topic_qos_t* d_qos, TopicQos* f_qos)
{
    if (d_qos == DAL_TOPIC_QOS_DEFAULT) {
        (*f_qos) = TOPIC_QOS_DEFAULT;
        return DAL_RETCODE_OK;
    }

    int seq_size = SIZE_SEQUENCE(octet, &d_qos->topic_data.value);
    if (0l != seq_size) {
        std::vector<eprosima::fastrtps::rtps::octet> vec;
        for (int i = 0; i < seq_size; i++) {
            octet val = 0;
            GET_ELEMENT_SEQUENCE(octet, &d_qos->topic_data.value, i, &val);
            vec.push_back(val);
        }
        f_qos->topic_data().data_vec(vec);
    }

    switch (d_qos->durability.kind) {
        default:
        case DAL_VOLATILE_DURABILITY_QOS:
            f_qos->durability().kind = VOLATILE_DURABILITY_QOS;
            break;
        case DAL_TRANSIENT_LOCAL_DURABILITY_QOS:
            f_qos->durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS;
            break;
        case DAL_TRANSIENT_DURABILITY_QOS:
            f_qos->durability().kind = TRANSIENT_DURABILITY_QOS;
            break;
        case DAL_PERSISTENT_DURABILITY_QOS:
            f_qos->durability().kind = PERSISTENT_DURABILITY_QOS;
            break;
    }

    f_qos->durability_service().service_cleanup_delay.seconds =
        d_qos->durability_service.service_cleanup_delay.sec;
    f_qos->durability_service().service_cleanup_delay.nanosec =
        d_qos->durability_service.service_cleanup_delay.nanosec;
    switch (d_qos->durability_service.history_kind) {
        default:
        case DAL_KEEP_LAST_HISTORY_QOS:
            f_qos->durability_service().history_kind = KEEP_LAST_HISTORY_QOS;
            break;
        case DAL_KEEP_ALL_HISTORY_QOS:
            f_qos->durability_service().history_kind = KEEP_ALL_HISTORY_QOS;
            break;
    }
    f_qos->durability_service().history_depth = d_qos->durability_service.history_depth;
    f_qos->durability_service().max_samples = d_qos->durability_service.max_samples;
    f_qos->durability_service().max_instances = d_qos->durability_service.max_instances;
    f_qos->durability_service().max_samples_per_instance =
        d_qos->durability_service.max_samples_per_instance;

    f_qos->deadline().period.seconds = d_qos->deadline.period.sec;
    f_qos->deadline().period.nanosec = d_qos->deadline.period.nanosec;

    f_qos->latency_budget().duration.seconds = d_qos->latency_budget.duration.sec;
    f_qos->latency_budget().duration.nanosec = d_qos->latency_budget.duration.nanosec;

    switch (d_qos->liveliness.kind) {
        default:
        case DAL_AUTOMATIC_LIVELINESS_QOS:
            f_qos->liveliness().kind = AUTOMATIC_LIVELINESS_QOS;
            break;
        case DAL_MANUAL_BY_PARTICIPANT_LIVELINESS_QOS:
            f_qos->liveliness().kind = MANUAL_BY_PARTICIPANT_LIVELINESS_QOS;
            break;
        case DAL_MANUAL_BY_TOPIC_LIVELINESS_QOS:
            f_qos->liveliness().kind = MANUAL_BY_TOPIC_LIVELINESS_QOS;
            break;
    }
    f_qos->liveliness().lease_duration.seconds = d_qos->liveliness.lease_duration.sec;
    f_qos->liveliness().lease_duration.nanosec = d_qos->liveliness.lease_duration.nanosec;

    switch (d_qos->reliability.kind) {
        default:
        case DAL_BEST_EFFORT_RELIABILITY_QOS:
            f_qos->reliability().kind = BEST_EFFORT_RELIABILITY_QOS;
            break;
        case DAL_RELIABLE_RELIABILITY_QOS:
            f_qos->reliability().kind = RELIABLE_RELIABILITY_QOS;
            break;
    }
    f_qos->reliability().max_blocking_time.seconds = d_qos->reliability.max_blocking_time.sec;
    f_qos->reliability().max_blocking_time.nanosec = d_qos->reliability.max_blocking_time.nanosec;

    switch (d_qos->destination_order.kind) {
        default:
        case DAL_BY_RECEPTION_TIMESTAMP_DESTINATIONORDER_QOS:
            f_qos->destination_order().kind = BY_RECEPTION_TIMESTAMP_DESTINATIONORDER_QOS;
            break;
        case DAL_BY_SOURCE_TIMESTAMP_DESTINATIONORDER_QOS:
            f_qos->destination_order().kind = BY_SOURCE_TIMESTAMP_DESTINATIONORDER_QOS;
            break;
    }

    switch (d_qos->history.kind) {
        default:
        case DAL_KEEP_LAST_HISTORY_QOS:
            f_qos->history().kind = KEEP_LAST_HISTORY_QOS;
            break;
        case DAL_KEEP_ALL_HISTORY_QOS:
            f_qos->history().kind = KEEP_ALL_HISTORY_QOS;
            break;
    }
    f_qos->history().depth = d_qos->history.depth;

    f_qos->resource_limits().max_samples = d_qos->resource_limits.max_samples;
    f_qos->resource_limits().max_instances = d_qos->resource_limits.max_instances;
    f_qos->resource_limits().max_samples_per_instance =
        d_qos->resource_limits.max_samples_per_instance;

    f_qos->transport_priority().value = d_qos->transport_priority.value;

    f_qos->lifespan().duration.seconds = d_qos->lifespan.duration.sec;
    f_qos->lifespan().duration.nanosec = d_qos->lifespan.duration.nanosec;

    switch (d_qos->ownership.kind) {
        default:
        case DAL_SHARED_OWNERSHIP_QOS:
            f_qos->ownership().kind = SHARED_OWNERSHIP_QOS;
            break;
        case DAL_EXCLUSIVE_OWNERSHIP_QOS:
            f_qos->ownership().kind = EXCLUSIVE_OWNERSHIP_QOS;
            break;
    }

    return DAL_RETCODE_OK;
}

dal_ret_t QosConverter::ConvertDataWriterQosFromDalToFastdds(dal_datawriter_qos_t* d_qos,
                                                             DataWriterQos* f_qos)
{
    if (d_qos == DAL_DATAWRITER_QOS_DEFAULT) {
        (*f_qos) = DATAWRITER_QOS_DEFAULT;
        return DAL_RETCODE_OK;
    }

    switch (d_qos->durability.kind) {
        default:
        case DAL_VOLATILE_DURABILITY_QOS:
            f_qos->durability().kind = VOLATILE_DURABILITY_QOS;
            break;
        case DAL_TRANSIENT_LOCAL_DURABILITY_QOS:
            f_qos->durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS;
            break;
        case DAL_TRANSIENT_DURABILITY_QOS:
            f_qos->durability().kind = TRANSIENT_DURABILITY_QOS;
            break;
        case DAL_PERSISTENT_DURABILITY_QOS:
            f_qos->durability().kind = PERSISTENT_DURABILITY_QOS;
            break;
    }

    f_qos->durability_service().service_cleanup_delay.seconds =
        d_qos->durability_service.service_cleanup_delay.sec;
    f_qos->durability_service().service_cleanup_delay.nanosec =
        d_qos->durability_service.service_cleanup_delay.nanosec;
    switch (d_qos->durability_service.history_kind) {
        default:
        case DAL_KEEP_LAST_HISTORY_QOS:
            f_qos->durability_service().history_kind = KEEP_LAST_HISTORY_QOS;
            break;
        case DAL_KEEP_ALL_HISTORY_QOS:
            f_qos->durability_service().history_kind = KEEP_ALL_HISTORY_QOS;
            break;
    }
    f_qos->durability_service().history_depth = d_qos->durability_service.history_depth;
    f_qos->durability_service().max_samples = d_qos->durability_service.max_samples;
    f_qos->durability_service().max_instances = d_qos->durability_service.max_instances;
    f_qos->durability_service().max_samples_per_instance =
        d_qos->durability_service.max_samples_per_instance;

    f_qos->deadline().period.seconds = d_qos->deadline.period.sec;
    f_qos->deadline().period.nanosec = d_qos->deadline.period.nanosec;

    f_qos->latency_budget().duration.seconds = d_qos->latency_budget.duration.sec;
    f_qos->latency_budget().duration.nanosec = d_qos->latency_budget.duration.nanosec;

    switch (d_qos->liveliness.kind) {
        default:
        case DAL_AUTOMATIC_LIVELINESS_QOS:
            f_qos->liveliness().kind = AUTOMATIC_LIVELINESS_QOS;
            break;
        case DAL_MANUAL_BY_PARTICIPANT_LIVELINESS_QOS:
            f_qos->liveliness().kind = MANUAL_BY_PARTICIPANT_LIVELINESS_QOS;
            break;
        case DAL_MANUAL_BY_TOPIC_LIVELINESS_QOS:
            f_qos->liveliness().kind = MANUAL_BY_TOPIC_LIVELINESS_QOS;
            break;
    }
    f_qos->liveliness().lease_duration.seconds = d_qos->liveliness.lease_duration.sec;
    f_qos->liveliness().lease_duration.nanosec = d_qos->liveliness.lease_duration.nanosec;

    switch (d_qos->reliability.kind) {
        default:
        case DAL_BEST_EFFORT_RELIABILITY_QOS:
            f_qos->reliability().kind = BEST_EFFORT_RELIABILITY_QOS;
            break;
        case DAL_RELIABLE_RELIABILITY_QOS:
            f_qos->reliability().kind = RELIABLE_RELIABILITY_QOS;
            break;
    }
    f_qos->reliability().max_blocking_time.seconds = d_qos->reliability.max_blocking_time.sec;
    f_qos->reliability().max_blocking_time.nanosec = d_qos->reliability.max_blocking_time.nanosec;

    switch (d_qos->destination_order.kind) {
        default:
        case DAL_BY_RECEPTION_TIMESTAMP_DESTINATIONORDER_QOS:
            f_qos->destination_order().kind = BY_RECEPTION_TIMESTAMP_DESTINATIONORDER_QOS;
            break;
        case DAL_BY_SOURCE_TIMESTAMP_DESTINATIONORDER_QOS:
            f_qos->destination_order().kind = BY_SOURCE_TIMESTAMP_DESTINATIONORDER_QOS;
            break;
    }

    switch (d_qos->history.kind) {
        default:
        case DAL_KEEP_LAST_HISTORY_QOS:
            f_qos->history().kind = KEEP_LAST_HISTORY_QOS;
            break;
        case DAL_KEEP_ALL_HISTORY_QOS:
            f_qos->history().kind = KEEP_ALL_HISTORY_QOS;
            break;
    }
    f_qos->history().depth = d_qos->history.depth;

    f_qos->resource_limits().max_samples = d_qos->resource_limits.max_samples;
    f_qos->resource_limits().max_instances = d_qos->resource_limits.max_instances;
    f_qos->resource_limits().max_samples_per_instance =
        d_qos->resource_limits.max_samples_per_instance;

    f_qos->transport_priority().value = d_qos->transport_priority.value;

    f_qos->lifespan().duration.seconds = d_qos->lifespan.duration.sec;
    f_qos->lifespan().duration.nanosec = d_qos->lifespan.duration.nanosec;

    int seq_size = SIZE_SEQUENCE(octet, &d_qos->user_data.value);
    if (0l != seq_size) {
        std::vector<eprosima::fastrtps::rtps::octet> vec;
        for (int i = 0; i < seq_size; i++) {
            octet val = 0;
            GET_ELEMENT_SEQUENCE(octet, &d_qos->user_data.value, i, &val);
            vec.push_back(val);
        }
        f_qos->user_data().data_vec(vec);
    }

    switch (d_qos->ownership.kind) {
        default:
        case DAL_SHARED_OWNERSHIP_QOS:
            f_qos->ownership().kind = SHARED_OWNERSHIP_QOS;
            break;
        case DAL_EXCLUSIVE_OWNERSHIP_QOS:
            f_qos->ownership().kind = EXCLUSIVE_OWNERSHIP_QOS;
            break;
    }

    f_qos->ownership_strength().value = d_qos->ownership_strength.value;

    f_qos->writer_data_lifecycle().autodispose_unregistered_instances =
        d_qos->writer_data_lifecycle.autodispose_unregistered_instances;

    // flow controller
    if (is_flow_controller_profiles_exist && !flow_controller_name.empty()) {
        f_qos->publish_mode().flow_controller_name = flow_controller_name.c_str();
    }

#ifdef DAL_FASTDDS_DATA_SHARING_DISABLED
    f_qos->data_sharing().off();
#endif

    return DAL_RETCODE_OK;
}

dal_ret_t QosConverter::ConvertDataReaderQosFromDalToFastdds(dal_datareader_qos_t* d_qos,
                                                             DataReaderQos* f_qos)
{
    if (d_qos == DAL_DATAREADER_QOS_DEFAULT) {
        (*f_qos) = DATAREADER_QOS_DEFAULT;
        return DAL_RETCODE_OK;
    }

    switch (d_qos->durability.kind) {
        default:
        case DAL_VOLATILE_DURABILITY_QOS:
            f_qos->durability().kind = VOLATILE_DURABILITY_QOS;
            break;
        case DAL_TRANSIENT_LOCAL_DURABILITY_QOS:
            f_qos->durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS;
            break;
        case DAL_TRANSIENT_DURABILITY_QOS:
            f_qos->durability().kind = TRANSIENT_DURABILITY_QOS;
            break;
        case DAL_PERSISTENT_DURABILITY_QOS:
            f_qos->durability().kind = PERSISTENT_DURABILITY_QOS;
            break;
    }

    f_qos->deadline().period.seconds = d_qos->deadline.period.sec;
    f_qos->deadline().period.nanosec = d_qos->deadline.period.nanosec;

    f_qos->latency_budget().duration.seconds = d_qos->latency_budget.duration.sec;
    f_qos->latency_budget().duration.nanosec = d_qos->latency_budget.duration.nanosec;

    switch (d_qos->liveliness.kind) {
        default:
        case DAL_AUTOMATIC_LIVELINESS_QOS:
            f_qos->liveliness().kind = AUTOMATIC_LIVELINESS_QOS;
            break;
        case DAL_MANUAL_BY_PARTICIPANT_LIVELINESS_QOS:
            f_qos->liveliness().kind = MANUAL_BY_PARTICIPANT_LIVELINESS_QOS;
            break;
        case DAL_MANUAL_BY_TOPIC_LIVELINESS_QOS:
            f_qos->liveliness().kind = MANUAL_BY_TOPIC_LIVELINESS_QOS;
            break;
    }
    f_qos->liveliness().lease_duration.seconds = d_qos->liveliness.lease_duration.sec;
    f_qos->liveliness().lease_duration.nanosec = d_qos->liveliness.lease_duration.nanosec;

    switch (d_qos->reliability.kind) {
        default:
        case DAL_BEST_EFFORT_RELIABILITY_QOS:
            f_qos->reliability().kind = BEST_EFFORT_RELIABILITY_QOS;
            break;
        case DAL_RELIABLE_RELIABILITY_QOS:
            f_qos->reliability().kind = RELIABLE_RELIABILITY_QOS;
            break;
    }
    f_qos->reliability().max_blocking_time.seconds = d_qos->reliability.max_blocking_time.sec;
    f_qos->reliability().max_blocking_time.nanosec = d_qos->reliability.max_blocking_time.nanosec;

    switch (d_qos->destination_order.kind) {
        default:
        case DAL_BY_RECEPTION_TIMESTAMP_DESTINATIONORDER_QOS:
            f_qos->destination_order().kind = BY_RECEPTION_TIMESTAMP_DESTINATIONORDER_QOS;
            break;
        case DAL_BY_SOURCE_TIMESTAMP_DESTINATIONORDER_QOS:
            f_qos->destination_order().kind = BY_SOURCE_TIMESTAMP_DESTINATIONORDER_QOS;
            break;
    }

    switch (d_qos->history.kind) {
        default:
        case DAL_KEEP_LAST_HISTORY_QOS:
            f_qos->history().kind = KEEP_LAST_HISTORY_QOS;
            break;
        case DAL_KEEP_ALL_HISTORY_QOS:
            f_qos->history().kind = KEEP_ALL_HISTORY_QOS;
            break;
    }
    f_qos->history().depth = d_qos->history.depth;

    f_qos->resource_limits().max_samples = d_qos->resource_limits.max_samples;
    f_qos->resource_limits().max_instances = d_qos->resource_limits.max_instances;
    f_qos->resource_limits().max_samples_per_instance =
        d_qos->resource_limits.max_samples_per_instance;

    int seq_size = SIZE_SEQUENCE(octet, &d_qos->user_data.value);
    if (0l != seq_size) {
        std::vector<eprosima::fastrtps::rtps::octet> vec;
        for (int i = 0; i < seq_size; i++) {
            octet val = 0;
            GET_ELEMENT_SEQUENCE(octet, &d_qos->user_data.value, i, &val);
            vec.push_back(val);
        }
        f_qos->user_data().data_vec(vec);
    }

    switch (d_qos->ownership.kind) {
        default:
        case DAL_SHARED_OWNERSHIP_QOS:
            f_qos->ownership().kind = SHARED_OWNERSHIP_QOS;
            break;
        case DAL_EXCLUSIVE_OWNERSHIP_QOS:
            f_qos->ownership().kind = EXCLUSIVE_OWNERSHIP_QOS;
            break;
    }

    f_qos->time_based_filter().minimum_separation.seconds =
        d_qos->time_based_filter.minimum_separation.sec;
    f_qos->time_based_filter().minimum_separation.nanosec =
        d_qos->time_based_filter.minimum_separation.nanosec;

    f_qos->reader_data_lifecycle().autopurge_no_writer_samples_delay.seconds =
        d_qos->reader_data_lifecycle.autopurge_nowriter_samples_delay.sec;
    f_qos->reader_data_lifecycle().autopurge_no_writer_samples_delay.nanosec =
        d_qos->reader_data_lifecycle.autopurge_nowriter_samples_delay.nanosec;
    f_qos->reader_data_lifecycle().autopurge_disposed_samples_delay.seconds =
        d_qos->reader_data_lifecycle.autopurge_disposed_samples_delay.sec;
    f_qos->reader_data_lifecycle().autopurge_disposed_samples_delay.nanosec =
        d_qos->reader_data_lifecycle.autopurge_disposed_samples_delay.nanosec;

#ifdef DAL_FASTDDS_DATA_SHARING_DISABLED
    f_qos->data_sharing().off();
#endif
    return DAL_RETCODE_OK;
}